home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume19 / fbm / part08 < prev    next >
Encoding:
Internet Message Format  |  1989-06-08  |  27.9 KB

  1. Subject:  v19i054:  FBM, image manipulation library, Part08/08
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Michael.Mauldin@NL.CS.CMU.EDU
  7. Posting-number: Volume 19, Issue 54
  8. Archive-name: fbm/part08
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 8 (of 8)."
  17. # Contents:  fbquant.c
  18. # Wrapped by rsalz@fig.bbn.com on Fri Jun  9 08:38:31 1989
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f 'fbquant.c' -a "${1}" != "-c" ; then 
  21.   echo shar: Will not clobber existing file \"'fbquant.c'\"
  22. else
  23. echo shar: Extracting \"'fbquant.c'\" \(26110 characters\)
  24. sed "s/^X//" >'fbquant.c' <<'END_OF_FILE'
  25. X/****************************************************************
  26. X * fbquant.c: FBM Library 0.93 (Beta test) 03-May-89  Michael Mauldin
  27. X *
  28. X * Copyright (C) 1989 by Michael Mauldin.  Permission is granted to
  29. X * use this file in whole or in part provided that you do not sell it
  30. X * for profit and that this copyright notice is retained unchanged.
  31. X *
  32. X * fbquant: Convert an RGB color image to mapped color format (color
  33. X *        quantization step).  Floyd-Steinberg dithering is used
  34. X *        to reduce color banding.  The quantization used is a
  35. X *        modification of Heckbert's median cut.
  36. X *
  37. X * USAGE
  38. X *    % fbquant [ -c<numcolors> ] [ -<type> ] < rgb > mapped
  39. X *
  40. X * EDITLOG
  41. X *    LastEditDate = Wed May  3 21:55:36 1989 - Michael Mauldin
  42. X *    LastFileName = /usr2/mlm/src/misc/fbm/fbquant.c
  43. X *
  44. X * HISTORY
  45. X * 03-May-89  Michael Mauldin (mlm) at Carnegie Mellon University
  46. X *    Clear FBM pointers before allocate
  47. X *
  48. X * 07-Apr-89  Michael Mauldin (mlm) at Carnegie Mellon University
  49. X *    Add provision for using colormap from another image.
  50. X *
  51. X * 07-Mar-89  Michael Mauldin (mlm) at Carnegie Mellon University
  52. X *    Beta release (version 0.93) mlm@cs.cmu.edu
  53. X *
  54. X * 26-Feb-89  Michael Mauldin (mlm) at Carnegie Mellon University
  55. X *    Changes for small color maps.  Fixed bug with unsigned
  56. X *    arithmetic that ruined dithering for images with small
  57. X *    colormaps.  Added error limiting in the Floyd-Steinberg
  58. X *    code to prevent color "shadowing" that occurs with small
  59. X *    numbers of colors.  Also change to use colors 0..n-1 instead
  60. X *    of reserving colors 0 and n-1 for Sun foreground/background
  61. X *    colors.
  62. X *
  63. X * 11-Nov-88  Michael Mauldin (mlm) at Carnegie Mellon University
  64. X *    Created.
  65. X *
  66. X * References: Uses a variant of Heckbert's adaptive partitioning
  67. X *           algorithm.  See Computer Graphics, v16n3 July 1982
  68. X ****************************************************************/
  69. X
  70. X# include <stdio.h>
  71. X# include "fbm.h"
  72. X
  73. Xint cmp_red(), cmp_grn(), cmp_blu(), cmp_cmap(), cmp_int();
  74. X
  75. X# define RD 0
  76. X# define GR 1
  77. X# define BL 2
  78. X# define REDMASK 0076000
  79. X# define REDSHFT 10
  80. X# define GRNMASK 0001740
  81. X# define GRNSHFT 5
  82. X# define BLUMASK 0000037
  83. X# define BLUSHFT 0
  84. X# define CUBITS  5
  85. X# define CUBIGN  (8-CUBITS)
  86. X# define CUBSID  32
  87. X# define CUBSIZ  32768
  88. X# define MAXSHRT 32767
  89. X# define MAXERR     32
  90. X
  91. X# define GETR(X) (((X) & REDMASK) >> REDSHFT)
  92. X# define GETG(X) (((X) & GRNMASK) >> GRNSHFT)
  93. X# define GETB(X)  ((X) & BLUMASK)
  94. X
  95. X# define CLRINDEX(R,G,B)            \
  96. X    (((R) << REDSHFT) & REDMASK |        \
  97. X     ((G) << GRNSHFT) & GRNMASK |        \
  98. X     ((B)  & BLUMASK))
  99. X
  100. X# define CLRINDEX8(R,G,B)            \
  101. X    (((R) << (REDSHFT-CUBIGN)) & REDMASK |    \
  102. X     ((G) << (GRNSHFT-CUBIGN)) & GRNMASK |    \
  103. X     ((B) >> (CUBIGN))  & BLUMASK)
  104. X
  105. X# define GETR8(X) (((X) & REDMASK) >> (REDSHFT-CUBIGN))
  106. X# define GETG8(X) (((X) & GRNMASK) >> (GRNSHFT-CUBIGN))
  107. X# define GETB8(X) (((X) & BLUMASK) << CUBIGN)
  108. X
  109. Xtypedef struct cstruct {
  110. X    unsigned char rd, gr, bl, indx;
  111. X} COLOR;
  112. X
  113. XCOLOR *cmap = NULL;
  114. X
  115. Xtypedef struct pix_struct {
  116. X    short cnt;
  117. X    short color;
  118. X} PIXEL;
  119. X
  120. Xint debug=0, verbose=0, colors=256, showcolor=0, dither=1;
  121. X
  122. X/****************************************************************
  123. X * main
  124. X ****************************************************************/
  125. X
  126. X# define USAGE "Usage: fbquant [ -c<numcolors> ] [ -<type> ] < rgb > mapped"
  127. X
  128. X#ifndef lint
  129. Xstatic char *fbmid = 
  130. X    "$FBM fbquant.c <0.93> 07-Mar-89  (C) 1989 by Michael Mauldin$";
  131. X#endif
  132. X
  133. Xmain (argc, argv)
  134. Xchar *argv[];
  135. X{ FBM input, output, mapimage;    /* Images */
  136. X  int hist[CUBSIZ];        /* Color cube 32x32x32 for histogram */
  137. X  int outtype = DEF_8BIT;    /* Output format desired */
  138. X  char *mapfile = NULL;        /* Name of file to copy map from */
  139. X  register int c;
  140. X
  141. X  /* Clear pointers */
  142. X  input.bm = input.cm = (unsigned char *) NULL;
  143. X  output.bm = output.cm = (unsigned char *) NULL;
  144. X  mapimage.bm = mapimage.cm = (unsigned char *) NULL;
  145. X
  146. X  /* Get the options */
  147. X  while (--argc > 0 && (*++argv)[0] == '-')
  148. X  { while (*++(*argv))
  149. X    { switch (**argv)
  150. X      { case 'c':    colors = atoi (*argv+1); SKIPARG; break;
  151. X        case 'm':    mapfile = *argv+1; SKIPARG; break;
  152. X    case 'n':    dither = 0;
  153. X    case 'd':    debug++; break;
  154. X    case 'D':    showcolor++; break;
  155. X    case 'v':    verbose++; break;
  156. X    case 'A':    outtype = FMT_ATK; break;
  157. X    case 'B':    outtype = FMT_FACE; break;
  158. X    case 'F':    outtype = FMT_FBM; break;
  159. X    case 'G':    outtype = FMT_GIF; break;
  160. X    case 'I':    outtype = FMT_IFF; break;
  161. X    case 'L':    outtype = FMT_LEAF; break;
  162. X    case 'M':    outtype = FMT_MCP; break;
  163. X    case 'P':    outtype = FMT_PBM; break;
  164. X    case 'S':    outtype = FMT_SUN; break;
  165. X    case 'T':    outtype = FMT_TIFF; break;
  166. X    case 'X':    outtype = FMT_X11; break;
  167. X    case 'Z':    outtype = FMT_PCX; break;
  168. X    default:    fprintf (stderr, "%s\n", USAGE);
  169. X            exit (1);
  170. X      }
  171. X    }
  172. X  }
  173. X  
  174. X  /* Check arguments */
  175. X  if (colors > 256 || colors < 8)
  176. X  { fprintf (stderr, "fbquant can only handle 8..256 colors\n");
  177. X    exit (1);
  178. X  }
  179. X
  180. X  /* Open file if name given as argument */
  181. X  if (! read_bitmap (&input, (argc > 0) ? argv[0] : (char *) NULL))
  182. X  { exit (1); }
  183. X
  184. X  /* Read colormap from map image (if specified) */
  185. X  if (mapfile != NULL)
  186. X  { if (!read_bitmap (&mapimage, mapfile))
  187. X    { perror (mapfile); exit (1); }
  188. X    
  189. X    if (mapimage.hdr.clrlen == 0)
  190. X    { fprintf (stderr, "fbquant: %s: no map\n", mapfile); exit (1); }
  191. X    
  192. X    /* Dont care about the map image, just its colormap */
  193. X    free (mapimage.bm); mapimage.bm = NULL;
  194. X    
  195. X    colors = mapimage.hdr.clrlen / 3;
  196. X    cmap = (COLOR *) malloc ((unsigned) colors * sizeof (COLOR));
  197. X    
  198. X    for (c=0; c<colors; c++)
  199. X    { cmap[c].rd = mapimage.cm[c];
  200. X      cmap[c].gr = mapimage.cm[c+colors];
  201. X      cmap[c].bl = mapimage.cm[c+colors*2];
  202. X      cmap[c].indx = c;
  203. X    }
  204. X    
  205. X    fprintf (stderr, "Quantizing \"%s\" [%dx%d] with %d colors from %s\n",
  206. X         input.hdr.title, input.hdr.cols, input.hdr.rows, colors, mapfile);
  207. X  }
  208. X  else
  209. X  { fprintf (stderr, "Quantizing \"%s\" [%dx%d] with %d colors\n",
  210. X         input.hdr.title, input.hdr.cols, input.hdr.rows, colors);
  211. X  }
  212. X
  213. X  if (input.hdr.planes != 3 || input.hdr.bits != 8)
  214. X  { fprintf (stderr, "fbquant can only handle 24bit RGB inputs\n");
  215. X    exit (1);
  216. X  }
  217. X
  218. X  /* Now build header for output bit map */
  219. X  output.hdr = input.hdr;
  220. X  output.hdr.planes = 1;
  221. X  output.hdr.clrlen = 3 * colors;
  222. X  
  223. X  /* Allocate space for output */
  224. X  alloc_fbm (&output);
  225. X
  226. X  /* Build colormap by sampling and mcut if not specified */
  227. X  if (mapfile == NULL)
  228. X  {
  229. X    cmap = (COLOR *) malloc ((unsigned) colors * sizeof (COLOR));
  230. X    sample_image (&input, hist);
  231. X    build_colormap (hist, cmap, colors);
  232. X  }
  233. X  
  234. X  /* Use Floyd-Steinberg error diffusion to quantize using the new cmap */
  235. X  clr_quantize (&input, &output, cmap, colors);
  236. X  
  237. X  /* Write out the result */
  238. X  if (write_bitmap (&output, stdout, outtype))
  239. X  { exit (0); }
  240. X
  241. X  exit (1);
  242. X}
  243. X
  244. X/****************************************************************
  245. X * sample_image:
  246. X ****************************************************************/
  247. X
  248. Xsample_image (image, hist)
  249. XFBM *image;
  250. Xint *hist;
  251. X{ register int i, j;
  252. X  register unsigned char *rp, *gp, *bp;
  253. X  int width = image->hdr.cols, height = image->hdr.rows;
  254. X  int rowlen = image->hdr.rowlen, plnlen = image->hdr.plnlen;
  255. X  int used=0;
  256. X  
  257. X  /* Clear the histogram */
  258. X  for (i=0; i<CUBSIZ; i++) hist[i] = 0;
  259. X  
  260. X  /* Now count */
  261. X  for (j=0; j<height; j++)
  262. X  { rp = &(image->bm[j*rowlen]);
  263. X    gp = rp+plnlen;
  264. X    bp = gp+plnlen;
  265. X
  266. X    for (i=0; i<width; i++)
  267. X    { if (++hist[ CLRINDEX8 (*rp++, *gp++, *bp++) ] == 1) used++; }
  268. X  }
  269. X
  270. X  if (debug)
  271. X  { fprintf (stderr, "Done counting, used %d colors for %d pixels\n",
  272. X         used, width*height);
  273. X  }
  274. X}
  275. X
  276. X/****************************************************************
  277. X * build_colormap:
  278. X ****************************************************************/
  279. X
  280. X# define SWAP(A,B) (t=A,A=B,B=t)
  281. X
  282. Xbuild_colormap (hist, cmap, colors)
  283. Xint *hist;
  284. XCOLOR *cmap;
  285. Xint colors;
  286. X{ register int i, k;
  287. X  PIXEL box[CUBSIZ];
  288. X  register PIXEL *b;
  289. X  int used=0, t;
  290. X
  291. X  /* Build the first box, encompassing all pixels */  
  292. X  for (b=box, i=0; i<CUBSIZ; i++)
  293. X  { b->color = i;
  294. X    k = hist[i];
  295. X    b->cnt = (k > MAXSHRT) ? MAXSHRT : k;
  296. X    b++;
  297. X  }
  298. X  
  299. X  /* Move all non-zero count pixels to the front of the list */
  300. X  for (i=0, used=0; i<CUBSIZ; i++)
  301. X  { if (box[i].cnt > 0) box[used++] = box[i]; }
  302. X
  303. X  /*-------- Special case if we didnt need all colors --------*/
  304. X  if (used <= colors)
  305. X  {
  306. X    /* Copy the colors actually found */
  307. X    for (i=0; i<used; i++)
  308. X    { cmap[i].rd = GETR8 (box[i].color);
  309. X      cmap[i].gr = GETG8 (box[i].color);
  310. X      cmap[i].bl = GETB8 (box[i].color);
  311. X    }
  312. X
  313. X    /* Set the rest to WHITE */
  314. X    for (; i<colors; i++)
  315. X    { cmap[i].rd = 255;
  316. X      cmap[i].gr = 255;
  317. X      cmap[i].bl = 255;
  318. X    }
  319. X  }
  320. X  
  321. X  else                /* Build sets all colors */
  322. X  { split_box (box, used, 0, colors, cmap); }
  323. X  
  324. X  /*----------------------------------------------------------------
  325. X   * Now arrange the colors in the desired order.  Sun convention is that
  326. X   * color 0 is white and color n-1 is black, so we sort by increasing
  327. X   * intensity (why not?) and then swap the lightest and darkest colors
  328. X   */
  329. X
  330. X  /* Now sort 0..n-1 according to increasing intensity */
  331. X  qsort (cmap, colors, sizeof (* cmap), cmp_int);
  332. X
  333. X  /* Make first color lightest and last color darkest */
  334. X  SWAP (cmap[0].rd, cmap[colors-1].rd);
  335. X  SWAP (cmap[0].gr, cmap[colors-1].gr);
  336. X  SWAP (cmap[0].bl, cmap[colors-1].bl);
  337. X  
  338. X  /* Set the output indices */
  339. X  for (i=0; i<colors; i++) { cmap[i].indx = i; }
  340. X}
  341. X
  342. X/****************************************************************
  343. X * split_box: Basic recursive part of Heckberts adaptive partitioning
  344. X *          algorithm.
  345. X ****************************************************************/
  346. X
  347. Xsplit_box (box, boxlen, clr, numclr, cmap)
  348. XPIXEL *box;
  349. Xint boxlen, clr, numclr;
  350. XCOLOR *cmap;
  351. X{ int maxv[3], minv[3], numv[3];
  352. X  int pcnt[3][CUBSID];
  353. X  int sbox, snum, split, half, maxdif, dif;
  354. X  register PIXEL *top, *bot;
  355. X  int topw, botw;
  356. X  int red, grn, blu;
  357. X  register int i, c;
  358. X
  359. X  /* If numclr exceeds boxlen, we are in trouble */
  360. X  if (numclr > boxlen)
  361. X  { fprintf (stderr, "boxlen %d is less numclr %d, panic!\n than",
  362. X         boxlen, numclr);
  363. X    fflush (stderr);
  364. X    abort ();
  365. X  }
  366. X
  367. X  /* Base case: only one color, assign the average for this cell */
  368. X  if (numclr <= 1)
  369. X  { red = box_avg_red (box, boxlen);
  370. X    grn = box_avg_grn (box, boxlen);
  371. X    blu = box_avg_blu (box, boxlen);
  372. X    
  373. X    /* Map x to x+4, because the histogram maps values to multiples of 8 */
  374. X    cmap[clr].rd = red + 4;
  375. X    cmap[clr].gr = grn + 4;
  376. X    cmap[clr].bl = blu + 4;
  377. X    
  378. X    if (debug)
  379. X    { fprintf (stderr, "\t\tassigning color %d  <%d,%d,%d>  (%d)\n",
  380. X           clr, cmap[clr].rd, cmap[clr].gr, cmap[clr].bl,
  381. X           box_weight (box, boxlen));
  382. X    }
  383. X    
  384. X    return;
  385. X  }
  386. X
  387. X  /* Gather statistics about the boxes contents */
  388. X  minv[RD] = minv[GR] = minv[BL] = CUBSID;
  389. X  maxv[RD] = maxv[GR] = maxv[BL] = 0;
  390. X  numv[RD] = numv[GR] = numv[BL] = 0;
  391. X  for (i=0; i<CUBSID; i++) { pcnt[RD][i] = pcnt[GR][i] = pcnt[BL][i] = 0; }
  392. X  
  393. X  for (i=0; i<boxlen; i++)
  394. X  { c = box[i].color;
  395. X    red = GETR(c); grn = GETG(c); blu = GETB(c);
  396. X    
  397. X    if (red < minv[RD]) minv[RD] = red;
  398. X    if (red > maxv[RD]) maxv[RD] = red;
  399. X    if (grn < minv[GR]) minv[GR] = grn;
  400. X    if (grn > maxv[GR]) maxv[GR] = grn;
  401. X    if (blu < minv[BL]) minv[BL] = blu;
  402. X    if (blu > maxv[BL]) maxv[BL] = blu;
  403. X    
  404. X    if (++pcnt[RD][red] == 1) numv[RD]++;
  405. X    if (++pcnt[GR][grn] == 1) numv[GR]++;
  406. X    if (++pcnt[BL][blu] == 1) numv[BL]++;
  407. X  }
  408. X
  409. X  /* Special case, boxlen = numclr, just assign each box one color */
  410. X  if (boxlen == numclr)
  411. X  { for (i=0; i<boxlen; i++)
  412. X    { split_box (box+i, 1, clr+i, 1, cmap); }
  413. X    return;
  414. X  }
  415. X
  416. X  /* Pick a dimension to split */
  417. X  split = -1; maxdif = -1;
  418. X
  419. X  if ((dif = (maxv[RD] - minv[RD])) > maxdif) { maxdif = dif; split = RD; }
  420. X  if ((dif = (maxv[GR] - minv[GR])) > maxdif) { maxdif = dif; split = GR; }
  421. X  if ((dif = (maxv[BL] - minv[BL])) > maxdif) { maxdif = dif; split = BL; }
  422. X  
  423. X  /* Sort along the chosen dimension */
  424. X  switch (split)
  425. X  { case RD:    qsort (box, boxlen, sizeof (*box), cmp_red); break;
  426. X    case GR:    qsort (box, boxlen, sizeof (*box), cmp_grn); break;
  427. X    case BL:    qsort (box, boxlen, sizeof (*box), cmp_blu); break;
  428. X    default:    fprintf (stderr, "panic in split_box, split = -1\n");
  429. X        fflush (stderr); fflush (stdout); abort ();
  430. X  }
  431. X  
  432. X  /* 
  433. X   * Split at the median, but make sure there are at least numclr/2
  434. X   * different colors on each side of the split, to avoid wasting
  435. X   * colors.
  436. X   *
  437. X   * Note: need to keep in mind that when the box is large, dont split
  438. X   *       too close to one edge.
  439. X   */
  440. X   
  441. X  half = numclr / 2;
  442. X  top = box;        bot = box + (boxlen-1);
  443. X  topw = top->cnt;    botw = bot->cnt;
  444. X  
  445. X  /* Set top and bot to point to min/max feasible splits */
  446. X  while ((top-box)+1 < half)        { top++; topw += top->cnt; }
  447. X  while ((boxlen-(bot-box)) < half)    { bot--; botw += bot->cnt; }
  448. X
  449. X  /* Move top and bottom towards each other 1/8 of remaining distance */
  450. X  c = (bot-top) / 8;
  451. X  for (i=0; i<c; i++)            { top++; topw += top->cnt; }
  452. X  for (i=0; i<c; i++)            { bot--; botw += bot->cnt; }
  453. X
  454. X  /* Now search for median */
  455. X  while (top < bot)
  456. X  { if (topw < botw)            { top++; topw += top->cnt; }
  457. X    else                { bot--; botw += bot->cnt; }
  458. X  }
  459. X
  460. X  /* Decide which half gets the midpoint */
  461. X  if (topw > botw)            /* Median wants to go with top */
  462. X  { sbox = (top-box) + 1;
  463. X    snum = numclr - half;
  464. X  }
  465. X  else                    /* Median wants to go with bottom */
  466. X  { sbox = (top - box);
  467. X    snum = half;
  468. X  }
  469. X  
  470. X  /* Handle boundary conditions with number of colors vs box size */
  471. X  if (sbox == 0) sbox++;
  472. X  else if (sbox == boxlen) sbox--;
  473. X
  474. X  while (snum > sbox) snum--;
  475. X  while (numclr-snum > boxlen-sbox) snum++;
  476. X
  477. X# ifndef OPTIMIZE
  478. X  /* Check for boundary condition errors */
  479. X  if (snum <= 0 || snum >= numclr)
  480. X  { fprintf (stderr, "panic, using zero colors for box\n");
  481. X    fflush (stderr);
  482. X    abort ();
  483. X  }
  484. X
  485. X  if (boxlen-sbox < numclr-snum)
  486. X  { fprintf (stderr, "panic, about to used %d boxes for %d colors\n",
  487. X         boxlen-sbox, numclr-snum);
  488. X    fflush (stderr);
  489. X    abort ();
  490. X  }
  491. X
  492. X  if (sbox < snum)
  493. X  { fprintf (stderr, "panic, about to used %d boxes for %d colors\n",
  494. X         sbox, snum);
  495. X    fflush (stderr);
  496. X    abort ();
  497. X  }
  498. X# endif
  499. X
  500. X  if (debug)
  501. X  { int count = numclr, depth = 8;
  502. X    while (count > 0) { depth--; count /= 2; }
  503. X    for (i=0; i<depth; i++) fprintf (stderr, "  ");
  504. X    
  505. X    fprintf (stderr, "box [%d..%d|%d] r(%d,%d,%d) g(%d,%d,%d) b(%d,%d,%d) =>",
  506. X         0, boxlen-1, numclr,
  507. X         minv[RD], maxv[RD], numv[RD],
  508. X         minv[GR], maxv[GR], numv[GR],
  509. X         minv[BL], maxv[BL], numv[BL]);
  510. X    fprintf (stderr, " %c [%d..%d|%d] [%d..%d|%d]\n",
  511. X         "RGB"[split], 0, sbox-1, snum, sbox, boxlen-1, numclr-snum);
  512. X  }
  513. X
  514. X  /* Now recurse and split each sub-box */
  515. X  split_box (box,      sbox,          clr,      snum,        cmap);
  516. X  split_box (box+sbox, boxlen - sbox, clr+snum, numclr-snum, cmap);
  517. X}
  518. X
  519. X/****************************************************************
  520. X * box_weight: Determine the total count of a box
  521. X ****************************************************************/
  522. X
  523. Xbox_weight (box, boxlen)
  524. Xregister PIXEL *box;
  525. Xregister int boxlen;
  526. X{ register int sum = 0, i;
  527. X
  528. X  for (i=0; i<boxlen; i++)
  529. X  { sum += box[i].cnt; }
  530. X  
  531. X  return (sum);
  532. X}
  533. X
  534. X/****************************************************************
  535. X * box_avg_red: Determine the average red value [0..255] of a box
  536. X ****************************************************************/
  537. X
  538. Xbox_avg_red (box, boxlen)
  539. Xregister PIXEL *box;
  540. Xregister int boxlen;
  541. X{ register int sum = 0, n = 0, i;
  542. X
  543. X  for (i=0; i<boxlen; i++)
  544. X  { sum += GETR8(box[i].color); n++; }
  545. X  
  546. X  return (sum / n);
  547. X}
  548. X
  549. X/****************************************************************
  550. X * box_avg_grn: Determine the average grn value [0..255] of a box
  551. X ****************************************************************/
  552. X
  553. Xbox_avg_grn (box, boxlen)
  554. Xregister PIXEL *box;
  555. Xregister int boxlen;
  556. X{ register int sum = 0, n = 0, i;
  557. X
  558. X  for (i=0; i<boxlen; i++)
  559. X  { sum += GETG8(box[i].color); n++; }
  560. X  
  561. X  return (sum / n);
  562. X}
  563. X
  564. X/****************************************************************
  565. X * box_avg_blu: Determine the average blu value [0..255] of a box
  566. X ****************************************************************/
  567. X
  568. Xbox_avg_blu (box, boxlen)
  569. Xregister PIXEL *box;
  570. Xregister int boxlen;
  571. X{ register int sum = 0, n = 0, i;
  572. X
  573. X  for (i=0; i<boxlen; i++)
  574. X  { sum += GETB8(box[i].color); n++; }
  575. X  
  576. X  return (sum / n);
  577. X}
  578. X
  579. X
  580. X/****************************************************************
  581. X * sort by increasing red ( then green and blue )
  582. X ****************************************************************/
  583. X
  584. Xcmp_red (a, b)
  585. XPIXEL *a, *b;
  586. X{ register r;
  587. X
  588. X  if (r = GETR(a->color) - GETR(b->color))
  589. X  { return (r); }
  590. X  
  591. X  if (r = GETG(a->color) - GETG(b->color))
  592. X  { return (r); }
  593. X
  594. X  if (r = GETB(a->color) - GETB(b->color))
  595. X  { return (r); }
  596. X  
  597. X  return (0);
  598. X}
  599. X
  600. X/****************************************************************
  601. X * sort by increasing green ( then blue and red )
  602. X ****************************************************************/
  603. X
  604. Xcmp_grn (a, b)
  605. XPIXEL *a, *b;
  606. X{ register r;
  607. X
  608. X  if (r = GETG(a->color) - GETG(b->color))
  609. X  { return (r); }
  610. X
  611. X  if (r = GETB(a->color) - GETB(b->color))
  612. X  { return (r); }
  613. X  
  614. X  if (r = GETR(a->color) - GETR(b->color))
  615. X  { return (r); }
  616. X  
  617. X  return (0);
  618. X}
  619. X
  620. X/****************************************************************
  621. X * sort by increasing blue ( then red and green )
  622. X ****************************************************************/
  623. X
  624. Xcmp_blu (a, b)
  625. XPIXEL *a, *b;
  626. X{ register r;
  627. X
  628. X  if (r = GETB(a->color) - GETB(b->color))
  629. X  { return (r); }
  630. X  
  631. X  if (r = GETR(a->color) - GETR(b->color))
  632. X  { return (r); }
  633. X  
  634. X  if (r = GETG(a->color) - GETG(b->color))
  635. X  { return (r); }
  636. X
  637. X  return (0);
  638. X}
  639. X
  640. X/****************************************************************
  641. X * clr_quantize: Do Floyd Steinberg quantizing on the image
  642. X ****************************************************************/
  643. X
  644. Xclr_quantize (input, output, cmap, colors)
  645. XFBM *input, *output;
  646. XCOLOR *cmap;
  647. Xint colors;
  648. X{ int **cerr, **lerr, **terr;
  649. X  int width = input->hdr.cols, height = input->hdr.rows;
  650. X  int rowlen = input->hdr.rowlen, plnlen = input->hdr.plnlen;
  651. X  register int i, j;
  652. X  register int rd, gr, bl;
  653. X  int rderr, grerr, blerr, clr;
  654. X  unsigned char *rp, *gp, *bp, *obm;
  655. X
  656. X  /*-------- Copy colormap into output bitmap --------*/
  657. X  for (i=0; i<colors; i++)
  658. X  { output->cm[i]               = cmap[i].rd;
  659. X    output->cm[i+colors]        = cmap[i].gr;
  660. X    output->cm[i+colors+colors] = cmap[i].bl;
  661. X  }
  662. X
  663. X  /*
  664. X   * Precompute necessary nearest neighbor query info.  Note that we wait
  665. X   * until after copying the colormap, since init reorders it
  666. X   */
  667. X
  668. X  init_nearest (cmap, colors);
  669. X
  670. X  /*-------- Do halftoning --------*/
  671. X  cerr = (int **) malloc (3 * sizeof (int *));
  672. X  lerr = (int **) malloc (3 * sizeof (int *));
  673. X  cerr[RD] = (int *) malloc (width * sizeof (int));
  674. X  cerr[GR] = (int *) malloc (width * sizeof (int));
  675. X  cerr[BL] = (int *) malloc (width * sizeof (int));
  676. X  lerr[RD] = (int *) malloc (width * sizeof (int));
  677. X  lerr[GR] = (int *) malloc (width * sizeof (int));
  678. X  lerr[BL] = (int *) malloc (width * sizeof (int));
  679. X  
  680. X  /*-------- Just use the nearest color everywhere --------*/
  681. X  if (!dither)
  682. X  {
  683. X    for (j=0; j<height; j++)
  684. X    { rp = &(input->bm[j*rowlen]);
  685. X      gp = rp+plnlen;
  686. X      bp = gp+plnlen;
  687. X      obm = &(output->bm[j*rowlen]);
  688. X
  689. X      for (i=0; i<width; i++)
  690. X      { rd = *rp++; gr = *gp++; bl = *bp++;
  691. X
  692. X        obm[i] = cmap[nearest (rd, gr, bl, cmap, colors)].indx;
  693. X      }
  694. X    }
  695. X    
  696. X    return;
  697. X  }
  698. X
  699. X  /*------ Just use the nearest color around the left, right, and top ------*/
  700. X
  701. X  /* Top border */
  702. X  rp = input->bm; gp = rp+plnlen; bp = gp+plnlen;
  703. X  obm = output->bm;
  704. X  for (i=0; i<width; i++)
  705. X  { obm[i] = cmap[nearest (rp[i], gp[i], bp[i], cmap, colors)].indx; }
  706. X
  707. X  /* Left border */
  708. X  rp = input->bm; gp = rp+plnlen; bp = gp+plnlen;
  709. X  obm = output->bm;
  710. X  for (j=0; j<height; j++)
  711. X  { obm[j*rowlen] = cmap[nearest (rp[j*rowlen], gp[j*rowlen],
  712. X             bp[j*rowlen], cmap, colors)].indx; }
  713. X
  714. X  /* Right border */
  715. X  rp = &(input->bm[width-1]); gp = rp + plnlen; bp = gp + plnlen;
  716. X  obm = &(output->bm[width-1]);
  717. X  for (j=0; j<height; j++)
  718. X  { obm[j*rowlen] = cmap[nearest (rp[j*rowlen], gp[j*rowlen],
  719. X                  bp[j*rowlen], cmap, colors)].indx; }
  720. X
  721. X  /*-------- Clear error vectors --------*/
  722. X  for (i=0; i<width; i++)
  723. X  { cerr[RD][i] = cerr[GR][i] = cerr[BL][i] = 0;
  724. X    lerr[RD][i] = lerr[GR][i] = lerr[BL][i] = 0;
  725. X  }
  726. X
  727. X  /*-------- Major loop for Floyd-Steinberg --------*/
  728. X  for (j=1; j<height; j++)
  729. X  { rp = &(input->bm[j*rowlen+1]);
  730. X    gp = rp+plnlen;
  731. X    bp = gp+plnlen;
  732. X    obm = &(output->bm[j*rowlen+1]);
  733. X
  734. X    for (i=1; i<width-1; i++)
  735. X    { rd = *rp++; gr = *gp++; bl = *bp++;
  736. X
  737. X      /* Sum up errors using Floyd-Steinberg weights */
  738. X      rderr= cerr[RD][i-1] + 7*lerr[RD][i-1] + 5*lerr[RD][i] + 3*lerr[RD][i+1];
  739. X      grerr= cerr[GR][i-1] + 7*lerr[GR][i-1] + 5*lerr[GR][i] + 3*lerr[GR][i+1];
  740. X      blerr= cerr[BL][i-1] + 7*lerr[BL][i-1] + 5*lerr[BL][i] + 3*lerr[BL][i+1];
  741. X
  742. X      rderr >>= 4;    /* Divide by 16 */
  743. X      grerr >>= 4;    /* Divide by 16 */
  744. X      blerr >>= 4;    /* Divide by 16 */
  745. X
  746. X      /* Chose nearest color to adjusted RGB value */
  747. X      rd += rderr; gr += grerr; bl += blerr;
  748. X
  749. X      clr = nearest (rd, gr, bl, cmap, colors);
  750. X      *obm++ = cmap[clr].indx;
  751. X
  752. X      /* Compute accumulated error for this pixel */      
  753. X      rd -= (int) cmap[clr].rd;
  754. X      gr -= (int) cmap[clr].gr;
  755. X      bl -= (int) cmap[clr].bl;
  756. X      
  757. X      /* Limit error (avoids color shadows) */
  758. X      if (rd < -MAXERR || rd > MAXERR)  rd = (rd * 3) >> 2;
  759. X      if (gr < -MAXERR || gr > MAXERR)  gr = (gr * 3) >> 2;
  760. X      if (bl < -MAXERR || bl > MAXERR)  bl = (bl * 3) >> 2;
  761. X
  762. X      /* Store errors in error vectors */
  763. X      cerr[RD][i] = rd;
  764. X      cerr[GR][i] = gr;
  765. X      cerr[BL][i] = bl;
  766. X    }
  767. X    
  768. X    /* Swap error vectors */
  769. X    terr = lerr; lerr = cerr; cerr = terr;
  770. X  }
  771. X}
  772. X
  773. X/****************************************************************
  774. X * nearest: Choose nearest color
  775. X ****************************************************************/
  776. X
  777. Xshort cache[CUBSIZ];
  778. X
  779. Xinit_nearest (cmap, colors)
  780. XCOLOR *cmap;
  781. Xint colors;
  782. X{ register int i;
  783. X
  784. X  /* Initialize the cache */
  785. X  for (i=0; i<CUBSIZ; i++) { cache[i] = -1; }
  786. X  
  787. X  /* Sort the colormap by decreasing red, then green, then blue */
  788. X  qsort (cmap, colors, sizeof (COLOR), cmp_cmap);
  789. X
  790. X  if (debug || showcolor)
  791. X  { fprintf (stderr, "\nFinal colormap:\n\n");
  792. X    for (i=0; i<colors; i++)
  793. X    { fprintf (stderr, "%3d:  <%3d,%3d,%3d> [%d]\n", 
  794. X           i, cmap[i].rd, cmap[i].gr, cmap[i].bl, cmap[i].indx);
  795. X    }
  796. X  }
  797. X}
  798. X
  799. X/* Fast square macro, uses local variable to avoid mulitple eval of X */
  800. X# define SQR(X) (sqrtmp = (X), sqrtmp*sqrtmp)
  801. X
  802. X/* Fast distance macro */
  803. X# define CDIST(CPTR,CR,CG,CB)        \
  804. X(sumtmp =  SQR (((int) ((CPTR)->rd)) - CR),        \
  805. X sumtmp += SQR (((int) ((CPTR)->gr)) - CG),        \
  806. X sumtmp += SQR (((int) ((CPTR)->bl)) - CB),        \
  807. X sumtmp)
  808. X
  809. X# define restrict(X) ((X) < 0 ? 0 : (X) > 255 ? 255 : (X))
  810. X
  811. Xnearest (rd, gr, bl, cmap, colors)
  812. Xint rd, gr, bl;
  813. XCOLOR *cmap;
  814. Xint colors;
  815. X{ register int clr, sqrtmp, sumtmp;
  816. X  register COLOR *a, *b, *c, *best, *ctail;
  817. X  int cindx, bestd, dist;
  818. X
  819. X  rd = restrict (rd);
  820. X  gr = restrict (gr);
  821. X  bl = restrict (bl);
  822. X
  823. X  /* Find array index in cache */
  824. X  cindx = CLRINDEX8 (rd, gr, bl);
  825. X
  826. X  /* Check cache for color value */  
  827. X  if ((clr = cache[cindx]) >= 0)
  828. X  { return (clr); }
  829. X  
  830. X  /*
  831. X   * Search through colormap for nearest neighbor:
  832. X   * 1) Find nearest red value by binary search
  833. X   * 2) Search up and down, keeping best point
  834. X   * 3) Terminate search when red difference is greater than best pt
  835. X   */
  836. X  
  837. X  /* Binary search for nearest red value */
  838. X  ctail = &cmap[colors];
  839. X  for (a=cmap, b= ctail-1;  a<b;  )  
  840. X  { c = a + (b-a)/2;    /* Find midpoint */
  841. X
  842. X    if (debug && verbose)
  843. X    { fprintf (stderr, "Searching for %d, a=%d (%d), b=%d (%d), c=%d (%d)\n",
  844. X        rd, a-cmap, a->rd, b-cmap, b->rd, c-cmap, c->rd);
  845. X    }
  846. X
  847. X    if (c->rd == rd)
  848. X    { break; }
  849. X    else if (c->rd < rd)
  850. X    { a = ++c; }
  851. X    else
  852. X    { b = c; }
  853. X  }
  854. X
  855. X  /*
  856. X   * c now points to closest red value, search up and down for closer
  857. X   * Euclidean distance, and stop search when the red distance alone
  858. X   * exceeds the bext found.
  859. X   */
  860. X
  861. X  /* Set best and bestd to best red value */
  862. X  best = c;
  863. X  bestd = CDIST (c, rd, gr, bl);
  864. X
  865. X  if (debug && verbose)
  866. X    fprintf (stderr, "Found c=%d (%d)  dist = %d\n", c-cmap, c->rd, bestd);
  867. X
  868. X  /* a and b are search pointers up and down */
  869. X  a = c-1;
  870. X  b = c+1;
  871. X
  872. X  while (bestd > 0 && (a >= cmap || b < ctail))
  873. X  {
  874. X    if (debug && verbose)
  875. X    { fprintf (stderr, "  search: bestd %d, best %d|%d, a %d, b %d\n",
  876. X        bestd, best-cmap, best->indx, a-cmap, b-cmap);
  877. X    }
  878. X
  879. X    if (a >= cmap)
  880. X    { if ((dist = CDIST (a, rd, gr, bl)) < bestd)
  881. X      { best = a; bestd = dist; }
  882. X      
  883. X      if (SQR ((int) a->rd - rd) >= bestd) a = cmap-1;
  884. X      else                 a--;
  885. X    }
  886. X
  887. X    if (b < ctail)
  888. X    { if ((dist = CDIST (b, rd, gr, bl)) < bestd)
  889. X      { best = b; bestd = dist; }
  890. X      
  891. X      if (SQR ((int) b->rd - rd) >= bestd) b = ctail;
  892. X      else                 b++;
  893. X    }
  894. X  }
  895. X  
  896. X  if (best < cmap || best >= ctail)
  897. X  { perror ("returning invalid color\n");
  898. X    abort ();
  899. X  }
  900. X
  901. X  clr = (best - cmap);
  902. X  
  903. X  if (debug)
  904. X  { fprintf (stderr, "Nearest(%3d,%3d,%3d) => <%3d,%3d,%3d> [%d]\n",
  905. X        rd, gr, bl, best->rd, best->gr, best->bl, best->indx);
  906. X  }
  907. X
  908. X  return ((cache[cindx] = clr));  
  909. X}
  910. X
  911. X/****************************************************************
  912. X * sort colormap by decreasing red ( then green and blue )
  913. X ****************************************************************/
  914. X
  915. Xcmp_cmap (a, b)
  916. Xregister COLOR *a, *b;
  917. X{ register int r;
  918. X
  919. X  if (r = (a->rd - b->rd)) { return (r); }
  920. X  if (r = (a->gr - b->gr)) { return (r); }
  921. X  if (r = (a->bl - b->bl)) { return (r); }
  922. X  
  923. X  return (0);
  924. X}
  925. X
  926. X/****************************************************************
  927. X * sort colormap by increasing intensity (Use NTSC weights)
  928. X ****************************************************************/
  929. X
  930. X# define RW 299
  931. X# define GW 587
  932. X# define BW 114
  933. X
  934. Xcmp_int (a, b)
  935. Xregister COLOR *a, *b;
  936. X{ register int ai, bi;
  937. X
  938. X  ai = RW * a->rd + GW * a->gr + BW * a->bl;
  939. X  bi = RW * b->rd + GW * b->gr + BW * b->bl;
  940. X
  941. X  return (ai - bi);
  942. X}
  943. END_OF_FILE
  944. if test 26110 -ne `wc -c <'fbquant.c'`; then
  945.     echo shar: \"'fbquant.c'\" unpacked with wrong size!
  946. fi
  947. # end of 'fbquant.c'
  948. fi
  949. echo shar: End of archive 8 \(of 8\).
  950. cp /dev/null ark8isdone
  951. MISSING=""
  952. for I in 1 2 3 4 5 6 7 8 ; do
  953.     if test ! -f ark${I}isdone ; then
  954.     MISSING="${MISSING} ${I}"
  955.     fi
  956. done
  957. if test "${MISSING}" = "" ; then
  958.     echo You have unpacked all 8 archives.
  959.     rm -f ark[1-9]isdone
  960. else
  961.     echo You still need to unpack the following archives:
  962.     echo "        " ${MISSING}
  963. fi
  964. ##  End of shell archive.
  965. exit 0
  966.